#region Copyright

// The contents of this file are subject to the Mozilla Public License
//  Version 1.1 (the "License"); you may not use this file except in compliance
//  with the License. You may obtain a copy of the License at
//  
//  http://www.mozilla.org/MPL/
//  
//  Software distributed under the License is distributed on an "AS IS"
//  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
//  License for the specific language governing rights and limitations under 
//  the License.
//  
//  The Initial Developer of the Original Code is Robert Smyth.
//  Portions created by Robert Smyth are Copyright (C) 2008.
//  
//  All Rights Reserved.

#endregion

using System;
using System.Collections.Generic;


namespace NUnit.Extensions.NUnitGridRunner
{
    public class CommandLineParser : ICommandLineParser
    {
        private readonly string[] args = new string[0];
        private readonly List<string> testAssemblies = new List<string>();
        private TimeSpan allTestsTimeLimit = TimeSpan.FromMinutes(5);
        private bool gridTest;
        private bool help;
        private bool ignorePriorFailures;
        private bool invalid;
        private bool nowarn;
        private TimeSpan runnerTimeLimit = TimeSpan.FromMinutes(2);
        private TimeSpan runnerTimeLoading = TimeSpan.FromSeconds(15);
        private string server = "localhost";
        private int serverPort = 9000;
        private bool simulate;
        private bool verbose;
        private string workingDirectory = ".";

        public CommandLineParser(string[] args)
        {
            this.args = args;
        }

        public IApplicationSettings Parse()
        {
            invalid = args.Length == 0;

            var commandArgs = new List<string>();
            commandArgs.AddRange(args);

            while (commandArgs.Count > 0 && !invalid)
            {
                string arg = ReplaceKeywords(commandArgs[0]);

                string[] command = arg.Split('=');

                if (command[0].StartsWith("/"))
                {
                    if (command.Length == 1)
                    {
                        ParseSwitchWithoutValue(command);
                    }
                    else if (command.Length == 2)
                    {
                        ParseSwitchWithValue(command);
                    }
                    else
                    {
                        invalid = true;
                    }
                }
                else if (command.Length == 1)
                {
                    testAssemblies.Add(command[0]);
                }
                else
                {
                    invalid = true;
                }

                if (invalid)
                {
                    Console.WriteLine("\nInvalid command option '{0}'", commandArgs[0]);
                }

                commandArgs.RemoveAt(0);
            }

            if (testAssemblies.Count == 0)
            {
                invalid = true;
                Console.WriteLine("No test assembly filename given.");
            }

            if (invalid || help)
            {
                ShowHelp();
            }

            return
                invalid
                    ? null
                    : new ApplicationSettings(server, serverPort, workingDirectory, testAssemblies.ToArray(), verbose,
                                              allTestsTimeLimit, runnerTimeLimit, simulate, ignorePriorFailures,
                                              gridTest,
                                              nowarn, runnerTimeLoading);
        }

        private static string ReplaceKeywords(string commandArg)
        {
            return commandArg.Replace("$COMPUTERNAME$", Environment.MachineName);
        }

        private void ParseSwitchWithValue(string[] command)
        {
            switch (command[0].ToLower())
            {
                case "/server":
                    {
                        server = command[1];
                        break;
                    }
                case "/serverport":
                    {
                        serverPort = int.Parse(command[1]);
                        break;
                    }
                case "/workingdirectory":
                    {
                        workingDirectory = command[1];
                        break;
                    }
                case "/allteststimelimit":
                    {
                        allTestsTimeLimit = TimeSpan.FromSeconds(int.Parse(command[1]));
                        break;
                    }
                case "/runnertimelimit":
                    {
                        runnerTimeLimit = TimeSpan.FromSeconds(int.Parse(command[1]));
                        break;
                    }
                case "/runnertimeloading":
                    {
                        runnerTimeLoading = TimeSpan.FromSeconds(int.Parse(command[1]));
                        break;
                    }
                default:
                    {
                        invalid = true;
                        break;
                    }
            }
        }

        private void ParseSwitchWithoutValue(string[] command)
        {
            switch (command[0].ToLower())
            {
                case "/verbose":
                    {
                        verbose = true;
                        break;
                    }
                case "/help":
                    {
                        help = true;
                        break;
                    }
                case "/simulate":
                    {
                        simulate = true;
                        break;
                    }
                case "/ignorepriorfailures":
                    {
                        ignorePriorFailures = true;
                        break;
                    }
                case "/gridtest":
                    {
                        gridTest = true;
                        break;
                    }
                case "/nowarn":
                    {
                        nowarn = true;
                        break;
                    }
                default:
                    {
                        invalid = true;
                        break;
                    }
            }
        }

        private static void ShowHelp()
        {
            Console.WriteLine(
                @"
Command line:

NUnitGridRunner.Console [testAssemblyFilename] [options]

Options:
 /workingDirectory=PATH      Path used for test fixture discovery. e.g. c:\Src\MyFiles\.
 /server=PCNAME              Name of the PC that hosts the grid manager. Default is 'localhost'.
 /serverPort=PORT            The port number used to connect to the grid manager. Default is 9000.
 /allTestsTimeLimit=SECONDS  Run time limit, in seconds, for the runner to complete. Default is 300.
 /runnerTimeLimit=SECONDS    Run time limit, in seconds, for each grid runner to complete. Default is 120.
 /runnerTimeLoading=SECONDS  Target run time for each grid runner task. 
                             Each grid runner taks is loaded with test fixtures to, if possible, fit this time.
                             A small time will result in more runner tasks. Default is 20 seconds.
                             A large time set the maximum time required to wait for the last grid runner to complete.
 /ignorePriorFailures        All grid runners will continue to run even if a prior runner's tests have failed.
 /verbose                    Enables verbose output.
 /nowarn                     Do not display warnings.
 /help                       Display this help.
 /simulate                   Run without starting grid runners.
 /gridTest                   Launch runners to test command line and network settings.
 

The keyword $COMPUTERNAME$ is replaced with the local computer's name.

Pressing any key during a run will abort the run.

Example:

  NUnitGridRunner.Console Tests.NUnitGridRunner.SampleTestAssembly01.dll /workingDirectory=C:\GoogleCode\NUnitGridRunner\Bin\Tests /runnersPathToBinaries=\\$COMPUTERNAME$\Tests

");
        }
    }
}